package dpsscorbainterface;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.rmi.RemoteException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.omg.CORBA.ORB;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;


import com.google.common.collect.ArrayListMultimap;

public class DpssImplementation extends corbainterfacePOA {

	int port=0;
	String server=null;
	
	/*
	count 1 = Offline account at NA
    count 2 = Offline account at EU
    count 3 = Offline account at AS
	count 4 = Online account at NA
    count 5 = Online account at EU
    count 6 = Online account at AS   
    */
	
	static int count1=0,count2=0,count3=0,count4=0,count5=0,count6=0,x=0;
	
	
    public ArrayListMultimap<String,Object>  myMultimap = ArrayListMultimap.create();
    public ConcurrentHashMap<String,String> onlineentry = new ConcurrentHashMap<String,String>();
	
        
    protected DpssImplementation(int portnumber) throws RemoteException
	{
		super();
		port=portnumber;
	}
	
	public String createPlayerAccount(String FirstName, String LastName, short Age, String Username,String Password, String IPAddress) {
		if (IPAddress.startsWith("132")){count1 = count1 +1 ;}
	   	 else if (IPAddress.startsWith("93")){count2 = count2 +1 ;}
		 else {count3 = count3 +1 ;}
			
			String message = "";
			final Object lock = new Object();
		try{
				synchronized(lock){
			if (!myMultimap.containsKey(Username)){
						myMultimap.put(Username, FirstName);
						myMultimap.put(Username, LastName);
						myMultimap.put(Username, Age);
						myMultimap.put(Username, Username);
						myMultimap.put(Username, Password);
				        myMultimap.put(Username, IPAddress);
				       
				      
				   } else {

					   return message + "Username already exists !" ; }
			
				}
		}
			catch(Exception e)
			{
				e.printStackTrace();
			}
			 this.logfile(Username,"Created Account at"+ " ");
			return message + "Successfully created!" ;

	}


	public String playerSignIn(String Username, String Password,String IPAddress) {
		 String message = "";
         if (myMultimap.containsKey(Username)){
         	if(myMultimap.containsValue(Password)){
         		if(onlineentry.get(Username) == null){
         			if (IPAddress.startsWith("132")){
         		onlineentry.put(Username,IPAddress);}
         			else if (IPAddress.startsWith("93")){
                 		onlineentry.put(Username,IPAddress);}
         			else {
                 		onlineentry.put(Username,IPAddress);}
         		if (IPAddress.startsWith("132")){count4 = count4 +1 ;}
            	 else if (IPAddress.startsWith("93")){count5 = count5 +1 ;}
            	 else {count6 = count6 +1 ;}

         		this.logfile(Username,"Signed in at"+ " ");
         		
         		return message + "You are signed in!";
         		
         		}
         		else { return message + "you are already signed in!"; }
         		}else {
         			this.logfile(Username,"Unauthorized access at"+ " ");
         			return message + "password is wrong!";}
         }else{
        	this.logfile(Username,"Unauthorized access at"+ " ");
         	return message + "Account doesn't exist!";
         	
         }
        
 		
	}

	
	public String playerSignOut(String Username,String IPAddress) {
		 String message = "" ;
         
         if(onlineentry.containsKey(Username)){
         	
         	if(onlineentry.containsValue(IPAddress)){	 
        	 onlineentry.remove(Username);
         			 count4 = count4 - 1 ;
                      this.logfile(Username,"Signed out at"+ " ");
                      message = message + "Successfully Signed out!" ;
         		}else{
         			this.logfile(Username,"Unsuccessful Signed Out at"+ " ");
                     message = message + "Username is signed in from another server, please enter correct IP address." ;
         			
         		}
         	
            
         }else{
         	this.logfile(Username,"Unsuccessful Signed Out at");
             message = message + "Player is offline" ;
            }
         return message;
	}
	public synchronized int getlocalCountonline()
  	{
  		int noOfRecords = 0;
  		noOfRecords =onlineentry.size() ;
 // 		System.out.println("online hashtable at localonline:"+" "+onlineentry.keySet());
  		return noOfRecords ;
  		
  	}
	
	 public synchronized void increase(String name, String pass){
         onlineentry.put(name, pass);
        
 }

public synchronized int getlocalCountoffline()
    	{
    		if (port == 5789){
   	  		 x =count1 - count4;
   	  		}
   		if (port == 5799){
  	  		 x =count2 - count5;
  	  		}
   		if (port == 6000){
  	  		 x =count3 - count6;
  	  		}
//   		System.out.println("online hashtable at localoffline:"+" "+onlineentry.keySet());
   	return x;
    	}
	
	public void getPlayerStatus(String Username, String Password, String IPAddress) {
		String Server1 = null,Server2 = null;
		int ServerPort1=0,ServerPort2=0;
		if (IPAddress.startsWith("132"))
			{
				server="North";
				Server1 = "Europe";
				Server2 = "Asia";
				ServerPort1 = 5799;
				ServerPort2 = 6000;
				this.logfile(Username," accessed from NorthAmerica at");
			}
		else if (IPAddress.startsWith("93"))
		{
			server = "Europe";
			Server1 = "North";
			Server2 = "Asia";
			ServerPort1 = 5789;
			ServerPort2 = 6000;
			this.logfile(Username," accessed from Europe at");
		}
		else
		{
			server = "Asia";
			Server1 = "North";
			Server2 = "Europe";
			ServerPort1 = 5789;
			ServerPort2 = 5799;
			this.logfile(Username," accessed from Asia at");
			}
		int gn;
		int noOfRecords;
		gn = getlocalCountoffline();
		noOfRecords = getlocalCountonline();
		System.out.println(" ");
		System.out.println(server+":"+" "+noOfRecords+" "+"Online" + ", "+gn+" "+"Offline");
		sendUDP_request(Server1, ServerPort1, Server2, ServerPort2);
//		System.out.println("online hashtable at getplayer:"+" "+onlineentry.keySet());

	}

	private void sendUDP_request(String Server1 , int ServerPort1, String Server2, int ServerPort2)
	{
		
		int [] portNumbers = new int[]{ServerPort1,ServerPort2};
		for(int i =0; i<2;i++)
		{
			DatagramSocket clientSocket = null;
			try{
				clientSocket = new DatagramSocket();
				byte [] serverInfo = (Server1+ServerPort1).getBytes();
				InetAddress IP = InetAddress.getByName("localhost");
				DatagramPacket request = new DatagramPacket(serverInfo, serverInfo.length,IP,portNumbers[i]);
				clientSocket.send(request);
				byte [] ServerCount = new byte[1024];
				DatagramPacket reply = new DatagramPacket(ServerCount,ServerCount.length);
				clientSocket.receive(reply);
				
				System.out.println(new String(reply.getData()).trim());
				
			}catch(SocketException e){System.out.println("Socket:" +e.getMessage());
			}catch(IOException e){ System.out.println("IO: "+e.getMessage());
			}finally {if(clientSocket != null)clientSocket.close(); }
		}
	}
	
	private void sendUDP_requests(String Server1 , int ServerPort1,String UserName)
	{
		
			DatagramSocket clientSocket = null;
			try{
				String finalString = ""; 
				String Data[]= {"FirstName","LastName","Age","UserName","Password","IPAddress"}; 
				List<Object> array = myMultimap.get(UserName) ;
				System.out.println("ArraySize" + array.size());
				
				for(int i = 0 ; i < array.size() ; i++){
				
					finalString = finalString + Data[i] + ":" ;
					finalString = finalString +  array.get(i);
					
					if(i< array.size()- 1)
					finalString = finalString + "," ;
					
				}
				
				clientSocket = new DatagramSocket();
				byte [] serverInfo = finalString.getBytes() ;
				InetAddress IP = InetAddress.getByName("localhost");
				DatagramPacket request = new DatagramPacket(serverInfo, serverInfo.length,IP,ServerPort1);
				clientSocket.send(request);
				byte [] ServerCount = new byte[1024];
				DatagramPacket reply = new DatagramPacket(ServerCount,ServerCount.length);
				clientSocket.receive(reply);
			}catch(SocketException e){System.out.println("Socket:" +e.getMessage());
			}catch(IOException e){ System.out.println("IO: "+e.getMessage());
			}finally {if(clientSocket != null)clientSocket.close(); }
		
	}

	@Override
	public synchronized String transferAccount(String Username, String Password, String OldIPAddress, String NewIPAddress ) {
	
		String from ; 
		String to ;
		if(NewIPAddress.startsWith("132")){
			sendUDP_requests("North",5789,Username);
			count1++;
			count4++;
			to = "North";
		}else if(NewIPAddress.startsWith("93")){
			sendUDP_requests("Europe",5799,Username);
			count2++;
			count5++;
			to = "Europe";
		}
		else{
			count3++;
			if(onlineentry.containsKey(Username) && onlineentry.containsValue(NewIPAddress)){
			
			count6++;
				}
			sendUDP_requests("Asia",6000,Username);
			to = "Asia";

		}
		if(OldIPAddress.startsWith("132")){
			 count1 = count1 - 1;
  	  		 count4 = count4 - 1;
  	  		 from = "North";
		}
		else if(OldIPAddress.startsWith("93")){
			count2 = count2 - 1;
	  		 count5 = count5 - 1;
	  		from = "Europe";
		}
		else{
			count3 = count3 - 1;
  	  		count6 = count6 - 1 ;
  	  		from = "Asia";
		}
		
		// Accounts will be removed from multimap and online entries and will be added to new location from UDP.java file implementation.
	    myMultimap.removeAll(Username) ;
		
		Set<String> s = onlineentry.keySet();
		System.out.println("o.keyset() -- hash map key list printed:"+" "+ s);
		
		onlineentry.remove(Username);
		this.logfile(Username,"Successfully transferred from"+" "+ from +" "+ "to"+" " + to);
		return "Sucessfully transferred :";
	}

	@Override
	public String suspendAccount(String AdminUsername, String AdminPassword, String AdminIP, String UsernameToSuspend) {
	
		if(onlineentry.containsKey(UsernameToSuspend))
	{
			count6--;
					  }
		
		
		if(myMultimap.containsEntry(UsernameToSuspend,"182.32.32.32")){
		count3--;
		}
		if(myMultimap.containsEntry(UsernameToSuspend,"93.32.32.32")){
			count2--;
			}
		else{
			count1--;
			}
		 if (myMultimap.containsKey(UsernameToSuspend)){
			 myMultimap.removeAll(UsernameToSuspend) ;
			 onlineentry.remove(UsernameToSuspend);
			 
		 }
		 else{System.out.println("User doesn't exist:");}
		 this.logfile(AdminUsername," "+"Successfully removed" +" "+ UsernameToSuspend);
		
		return  " ";
	}

	
public static void main(String[] args) {
	
	try{
		ORB orb = ORB.init(args,null);
		POA rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
	DpssImplementation North = new DpssImplementation(5789);
	byte[] id1 = rootPOA.activate_object(North);
	org.omg.CORBA.Object ref1 = rootPOA.id_to_reference(id1);
	
	String ior1 = orb.object_to_string(ref1);	
	PrintWriter fileMontreal = new PrintWriter("ior1.txt");
	fileMontreal.println(ior1);
	fileMontreal.close();
	
	DpssImplementation Europe = new DpssImplementation(5799);
	byte[] id2 = rootPOA.activate_object(Europe);
	org.omg.CORBA.Object ref2 = rootPOA.id_to_reference(id2);
	
	String ior2 = orb.object_to_string(ref2);	
	PrintWriter file2 = new PrintWriter("ior2.txt");
	file2.println(ior2);
	file2.close();
	
	DpssImplementation Asia = new DpssImplementation(6000);
	byte[] id3 = rootPOA.activate_object(Asia);
	org.omg.CORBA.Object ref3 = rootPOA.id_to_reference(id3);
	
	String ior3 = orb.object_to_string(ref3);
	PrintWriter file3 = new PrintWriter("ior3.txt");
	file3.println(ior3);
	file3.close();
	System.out.println("Working"); 
	
	
	(new UDP("North",5789,North)).start();
	(new UDP("Europe",5799,Europe)).start();
	(new UDP("Asia",6000,Asia)).start();
	
	rootPOA.the_POAManager().activate();
	orb.run();
	
	}
	catch(Exception ex){
		ex.printStackTrace();
	}
}
		
	
	@SuppressWarnings("deprecation")
	public int getTiming(String Timing){
		
		Date logTime = new Date();
		int result ;
		
		if(Timing.equals("Hour")){
			result = logTime.getHours();
			
		}else if(Timing.equals("Minute")){
			result = logTime.getMinutes();
		}else{
			result = logTime.getSeconds();
		}
		return result ;
	}
	
	public void logfile(String Username,String info){
	    	File file;
	    	BufferedWriter bufferedwritter = null;
	    	try{
	    		file = new File(Username+".txt");
	    		if(!file.isFile()){
	    			file.createNewFile();
	    		}	
                        FileWriter fileWriter = new FileWriter(file.getName(),true);
		        bufferedwritter = new BufferedWriter(fileWriter);

		        bufferedwritter.newLine();
		        String  message  =  "Username:" + Username +  info;  
		        
		        int Hour = getTiming("Hour");
		        int Minute = getTiming("Minute");
		        int Second = getTiming("Seconds");
		     
		        message = message +"  " + "Hours" + Hour +"  "+ "Minutes" + Minute +"  "+ "Seconds" +"  " + Second; 
                        bufferedwritter.write(message);
	    	}

	    	catch(IOException ioe){
	    		System.out.println("IO Exceptions " +ioe);
	    			    	}
			catch(Exception e){
				System.out.println(" Un Known Exceptions " +e);
				}
			finally{
				try {
					if(bufferedwritter!=null){
						bufferedwritter.close();
					}
				}
				catch (IOException e) {

					e.printStackTrace();
				}
			}	
	    }
}
